# -*-coding:utf-8 -*-
#encoding=UTF-8
"""
采空区空洞流算法
"""
import random
from scipy.spatial.distance import pdist
import math
import matplotlib.pyplot as plt
import numpy as np
from lbm_solver import *

class GA(object):
    # 初始化种群 生成chromosome_length大小的population_size个个体的种群
    def __init__(self, population_size, chromosome_length, pc, pm, max_gen):
        self.population_size = population_size
        self.choromosome_length = chromosome_length
        # self.population=[[]]
        self.pc = pc
        self.pm = pm
        # self.fitness_value=[]

    def species_origin(self):
        population = [[]]
        for i in range(self.population_size):
            temporary=np.zeros(self.choromosome_length)
            loction = np.loadtxt('1105-600point.txt',dtype=int)#读取填充块的位置，避免工作面和两个出口有块，造成结算失败
            Gene_position = random.randint(1,len(loction))
            change_ge = random.sample(range(0,len(loction)),Gene_position)
            loction1 = loction[change_ge]
            temporary[loction1]=1
            population.append(temporary)
        return population[1:]
        # 将种群返回，种群是个二维数组，个体和染色体两维

    def function(self, population, choromosome_length, target):
        # 定义适应值函数
        temporary = []
        function1 = []
        temporary = population
        temporary = np.array(temporary)
        n = temporary.shape[0]#个体数
        m = temporary.shape[1]#染色体数
        # print m
        # print n
        x = np.zeros(m)
        y = np.zeros(n)
        for i in range(n):
            print('正在计算第'+str(i+1)+'个个体')
            print('---------------------------------------------')
            inti = temporary[i, :]
            inti = np.array(inti)
            inti = inti.reshape(4,6)
            np.savetxt('inti.dat',inti,fmt='%d',delimiter='')#存储个体冒落形态，用于lbm_solver.py脚本读取
            lbmresult=np.zeros((1,86))
            lbmresult=lbmcode()#调用格子算法
            lbmresult=lbmresult.reshape(86)
            '''欧氏距离'''
            # tack=np.vstack([lbmresult,target])#计算欧氏距离
            # y[i]=pdist(tack)
            '''杰卡德相似度'''
            # y[i]=np.dot(lbmresult,target.reshape(86,1))/(np.sum(lbmresult**2)+np.sum(target**2)-np.dot(lbmresult,target.reshape(86,1)))
            '''相对误差'''
            for j in range(m):
                x[j]=abs(lbmresult[j]-target[0,j])/target[0,j]
            y[i]=x.sum()
        function1 = y
        return function1

    def fitness(self, function1):
        # 定义适应度
        fitness_value = []
        num = len(function1)
        for i in range(num):
            temporary = 1/(function1[i]+1)
            # temporary = function1[i]
            fitness_value.append(temporary)
        # 将适应度添加到列表中
        # print (fitness_value)
        return fitness_value

    # 计算适应度和
    def sum(self, fitness_value):
        total = 0
        for i in range(len(fitness_value)):
            total += fitness_value[i]
        return total

    def selection(self, population, fitness_value):
        #选择操作
        new_fitness = []
        # 将所有的适应度求和
        total_fitness = self.sum(fitness_value)
        for i in range(len(fitness_value)):
            new_fitness.append(fitness_value[i] / total_fitness)

        # 将所有个体的适应度正则化
        pp = np.cumsum(new_fitness)
        ms = []
        # 存活的种群
        pop_len = len(population)
        # 求出种群长度
        # 根据随机数确定哪几个能存活
        for i in range(pop_len):
            ms.append(random.random())

        # 产生种群个数的随机值
        # 存活的种群排序
        newin = 0
        new_pop = population
        # 轮盘赌方式
        while newin < pop_len:
            fitin = 0
            while fitin < pop_len:
                if ms[newin] < pp[fitin]:
                    break
                fitin += 1
            if fitin >= pop_len:
                fitin = pop_len - 1
            new_pop[newin] = population[fitin]
            newin += 1
        population = new_pop

    def crossover(self, population):
        # 交叉操作
        # pc是交叉概率
        pop_len = len(population)
        for i in range(pop_len - 1):
            if random.random() < self.pc:
                cpoint = random.randint(0, len(population[0]))
                # 在种群个数内随机生成单点交叉点
                temporary1 = []
                temporary2 = []
                temporary1.extend(population[i][0:cpoint])
                temporary1.extend(population[i + 1][cpoint:len(population[i])])
                # 将tmporary1作为暂存器，暂时存放第i个染色体中的前0到cpoint个基因，
                # 然后再把第i+1个染色体中的后cpoint到第i个染色体中的基因个数，补充到temporary2后面
                temporary2.extend(population[i + 1][0:cpoint])
                temporary2.extend(population[i][cpoint:len(population[i])])
                # 将tmporary2作为暂存器，暂时存放第i+1个染色体中的前0到cpoint个基因，
                # 然后再把第i个染色体中的后cpoint到第i个染色体中的基因个数，补充到temporary2后面
                population[i] = temporary1
                population[i + 1] = temporary2

    def mutation(self, population):
        #变异操作
        # pm是变异概率
        px = len(population)
        # 求出种群中所有种群/个体的个数
        py = len(population[0])
        # 染色体/个体基因的个数
        for i in range(px):
            if random.random() < self.pm:
                point = np.loadtxt('1105-600point.txt',dtype=int)#1105-600point.txt存储的是变异位置，避免入口和出口出现固体障碍物
                cpoint = random.sample(range(0, len(point)), 1)
                mpoint = point[cpoint]
                for j in range(1):
                    if population[i][mpoint[j]] == 1:
                        # 将mpoint个基因进行多点随机变异，变为0或者1
                        population[i][mpoint[j]] = 0
                    else:
                        population[i][mpoint[j]] = 1

    def best(self, population, fitness_value):
        #保存最优个体
        px = len(population)
        bestindividual = population[0]
        bestfitness = fitness_value[0]
        # print(fitness_value)
        for i in range(1, px):
            # 循环找出最大的适应度，适应度最大的也就是最好的个体
            if fitness_value[i] >= bestfitness:
                bestfitness = fitness_value[i]
                bestindividual = population[i]
        return [bestindividual, bestfitness]

    def main(self):
        #主函数
        results = [[]]
        target = np.loadtxt('target.txt', dtype=float)#读取LDA实验数据用于构建适应值
        target = target.reshape(1, 86)
        fitness_value = []
        averge_fitness = []
        max_fitness = []
        population = self.species_origin()
        for i in range(max_gen):
            print('迭代第'+str(i+1)+'次')
            print('---------------------------------------------')
            function_value = self.function(population, chromosome_length,
                                           target)
            # print('fit funtion_value:',function_value)
            fitness_value = self.fitness(function_value)
            self.selection(population, fitness_value)
            self.crossover(population)
            self.mutation(population)
            # print('fitness_value:',fitness_value)
            best_individual, best_fitness = self.best(population,
                                                      fitness_value)
            results.append([best_fitness, best_individual])
            print('最优适应值:'+str(best_fitness))
            print('---------------------------------------------')
            averge = np.mean(fitness_value)
            averge_fitness.append(averge)
            max_fit = max(fitness_value)
            max_fitness.append(max_fit)
            best_individual=np.array(best_individual)
            np.savetxt('generation'+str(i)+' bestind.txt',best_individual.reshape(4,6))
            if best_fitness>=0.95:
                break
        np.savetxt('best_fitness.txt',max_fitness)
        np.savetxt('averge_fitness.txt',averge_fitness)
        best_individual=np.array(best_individual)
        np.savetxt('best_individual.txt',best_individual.reshape(4,6))

if __name__ == '__main__':
    population_size = 100  #种群规模
    chromosome_length = 24  #个体长度
    pc = 0.8 #交叉概率
    pm = 0.1 #变异概率
    max_gen = 100 #进化代数
    ga = GA(population_size, chromosome_length, pc, pm, max_gen)
    ga.main()